/*
 * Copyright (c) 2001-2007, Inversoft, All Rights Reserved
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 */
package org.inversoft.vertigo.domain.commerce;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Currency;

/**
 * <p>
 * This class represents an internationalize monetary value.
 * </p>
 *
 * @author  Brian Pontarelli
 */
public class Money implements Serializable, Comparable<Money> {
    private final BigDecimal amount;
    private final Currency currency;

    private Money(String amount, Currency currency) {
        this.amount = new BigDecimal(amount);
        this.currency = currency;
    }

    private Money(BigDecimal amount, Currency currency) {
        this.amount = amount;
        this.currency = currency;
    }

    public static Money valueOf(String amount, Currency currency) {
        return new Money(amount, currency);
    }

    public static Money valueOf(BigDecimal amount, Currency currency) {
        return new Money(amount, currency);
    }

    public Currency getCurrency() {
        return currency;
    }

    public Money plus(Money that) {
        return new Money(amount.add(that.amount), currency);
    }

    public Money minus(Money that) {
        return new Money(amount.subtract(that.amount), currency);
    }

    public Money times(long l) {
        return new Money(amount.multiply(new BigDecimal(l)), currency);
    }

    public Money times(Double d) {
        return new Money(amount.multiply(new BigDecimal(d)), currency);
    }

    public Money dividedBy(long l) {
        return new Money(amount.divide(new BigDecimal(l)), currency);
    }


    public Money dividedBy(double d) {
        return new Money(amount.divide(new BigDecimal(d)), currency);
    }

    public long longValue() {
        return amount.longValue();
    }

    public double doubleValue() {
        return amount.doubleValue();
    }

    public String toNumericString() {
        BigDecimal bd = amount.setScale(2, RoundingMode.HALF_UP);
        return bd.toPlainString();
    }

    public String toString() {
        return currency.getSymbol() + toNumericString();
    }

    public boolean equalsRounded(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Money money = (Money) o;

        String str = toNumericString();
        String other = money.toNumericString();
        if (!str.equals(other)) return false;
        if (!currency.equals(money.currency)) return false;

        return true;
    }

    public boolean equalsExact(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Money money = (Money) o;

        if (!amount.equals(money.amount)) return false;
        if (!currency.equals(money.currency)) return false;

        return true;
    }

    public int compareTo(Money o) {
        if (!currency.equals(o.currency)) {
            throw new IllegalArgumentException("Unable to compare monies with different currencies");
        }

        return amount.compareTo(o.amount);
    }

    public int hashCode() {
        int result;
        result = amount.hashCode();
        result = 31 * result + currency.hashCode();
        return result;
    }
}